package com.googlecode.mapperdao

import com.googlecode.mapperdao.jdbc.Setup
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
import org.scalatest.{FunSuite, Matchers}

/**
 * @author kostantinos.kougios
 *
 *         23 Apr 2012
 */
@RunWith(classOf[JUnitRunner])
class ManyToOneLazyLoadSuite extends FunSuite with Matchers
{

	val (jdbc, mapperDao, queryDao) = Setup.setupMapperDao(List(PersonEntity, CompanyEntity))

	if (Setup.database == "h2") {
		val selectConfig = SelectConfig.lazyLoad

		test("query is lazy") {
			createTables
			val p1 = Person("Kostas", Company("Coders limited"))
			val p2 = Person("Manolis", Company("Designers"))
			val i1 = mapperDao.insert(PersonEntity, p1)
			val i2 = mapperDao.insert(PersonEntity, p2)
			import com.googlecode.mapperdao.Query._
			val l = queryDao.query(QueryConfig(lazyLoad = LazyLoad.all), select from PersonEntity)
			val s1 = l.head
			val s2 = l.last
			verifyNotLoadded(s1)
			verifyNotLoadded(s2)
			s1 should be(p1)
			s2 should be(p2)
		}

		test("update immutable entity, skip lazy loaded") {
			createTables
			val c1 = mapperDao.insert(CompanyEntity, Company("Coders limited"))
			val c2 = mapperDao.insert(CompanyEntity, Company("Designers limited"))
			val inserted = mapperDao.insert(PersonEntity, Person("Kostas", c1))
			val selected = mapperDao.select(selectConfig, PersonEntity, inserted.id).get
			val updated = mapperDao.update(UpdateConfig(skip = Set(PersonEntity.company)), PersonEntity, selected, Person("NotKostas", c2))
			verifyNotLoadded(selected)
			mapperDao.select(PersonEntity, inserted.id).get should be(Person("NotKostas", c1))
		}

		test("update mutable entity") {
			createTables
			val c1 = mapperDao.insert(CompanyEntity, Company("Coders limited"))
			val c2 = mapperDao.insert(CompanyEntity, Company("Designers limited"))
			val person = Person("Kostas", c1)
			val inserted = mapperDao.insert(PersonEntity, person)
			val selected = mapperDao.select(selectConfig, PersonEntity, inserted.id).get
			selected.company = c2
			selected.name = "x"
			val updated = mapperDao.update(PersonEntity, selected)
			updated should be(selected)
			mapperDao.select(PersonEntity, updated.id).get should be(updated)
		}

		test("update immutable entity") {
			createTables
			val c1 = mapperDao.insert(CompanyEntity, Company("Coders limited"))
			val c2 = mapperDao.insert(CompanyEntity, Company("Designers limited"))
			val person = Person("Kostas", c1)
			val inserted = mapperDao.insert(PersonEntity, person)
			val selected = mapperDao.select(selectConfig, PersonEntity, inserted.id).get
			val updatedPerson = Person("NotKostas", c2)
			val updated = mapperDao.update(PersonEntity, inserted, updatedPerson)
			updated should be(updatedPerson)
			mapperDao.select(selectConfig, PersonEntity, updated.id).get should be(updated)
		}

		test("manually updating them stops lazy loading") {
			createTables
			val person = Person("Kostas", Company("Coders limited"))
			val inserted = mapperDao.insert(PersonEntity, person)
			val selected = mapperDao.select(selectConfig, PersonEntity, inserted.id).get
			selected.company = Company("an other one")

			selected.company should be(Company("an other one"))
			verifyNotLoadded(selected)
		}

		test("select is lazy") {
			createTables
			val person = Person("Kostas", Company("Coders limited"))
			val inserted = mapperDao.insert(PersonEntity, person)
			inserted should be(person)
			val selected = mapperDao.select(selectConfig, PersonEntity, inserted.id).get
			verifyNotLoadded(selected)
			selected should be(inserted)
			selected.id should be > 0
		}
	}

	def verifyNotLoadded(o: Any) {
		val persisted = o.asInstanceOf[Persisted]
		persisted.mapperDaoValuesMap.isLoaded(PersonEntity.company) should be(false)
	}

	def createTables = {
		Setup.dropAllTables(jdbc)
		Setup.queries(this, jdbc).update("ddl")
	}

	case class Person(var name: String, var company: Company)

	case class Company(name: String)

	object PersonEntity extends Entity[Int, SurrogateIntId, Person]
	{
		val id = key("id") autogenerated (_.id)
		val name = column("name") to (_.name)
		val company = manytoone(CompanyEntity) getter ("company") to (_.company)

		def constructor(implicit m: ValuesMap) = new Person(name, company) with Stored
		{
			val id: Int = PersonEntity.id
		}
	}

	object CompanyEntity extends Entity[Int, SurrogateIntId, Company]
	{
		val id = key("id") autogenerated (_.id)
		val name = column("name") to (_.name)

		def constructor(implicit m: ValuesMap) = new Company(name) with Stored
		{
			val id: Int = CompanyEntity.id
		}
	}

}